export * from './fetch';
export * from './router';

export function sleep(delay: number): Promise<void> {
	return new Promise<void>((resolve) => {
		setTimeout(resolve, delay);
	});
}

export function downloadFile(data: Blob, filename: string) {
	const url = URL.createObjectURL(data);
	const el = document.createElement('a');
	el.style.display = 'none';
	el.download = filename;
	el.href = url;
	document.body.appendChild(el);
	el.click();
	URL.revokeObjectURL(url);
}

export function fromHTML(html: string): HTMLElement {
	const div = document.createElement('div');
	div.innerHTML = html;
	return div.firstElementChild as HTMLElement;
}

export function waitFor<T extends HTMLElement>(selector: string, waitSelector?: string, timeout: number = 5000, refEl: HTMLElement | ShadowRoot | Document = document): Promise<T | null> {
	return new Promise<T | null>((resolve) => {
		let disposeTimeout = 0;
		let disposeInterval = 0;

		disposeTimeout = setTimeout(() => {
			clearInterval(disposeInterval);
			resolve(null);
		}, timeout) as any;

		disposeInterval = setInterval(() => {
			let el = refEl.querySelector<T>(waitSelector ?? selector);
			if (!el) return;

			if (waitSelector) {
				el = refEl.querySelector<T>(selector);
				if (!el) return;
			}

			resolve(el);
			clearTimeout(disposeTimeout);
			clearInterval(disposeInterval);
		}, 10) as any;
	});
}

export function observe<T extends HTMLElement>(selector: string, cb: (el: T) => any): void {
	const observer = new MutationObserver((mutations) => {
		for (const mutation of mutations) {
			for (const node of Array.from(mutation.addedNodes)) {
				if (!(node instanceof Element)) continue;
				if (!node.matches(selector)) continue;

				cb(node as T);
				return;
			}
		}
	});

	observer.observe(document.body, {
		childList: true,
		subtree: true
	});
}
